/*
 * Alloy Analyzer
 * Copyright (c) 2002 Massachusetts Institute of Technology
 *
 * The Alloy Analyzer is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * The Alloy Analyzer is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the Alloy Analyzer; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package alloy.viz.alloyviz;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

import alloy.api.AlloyEvaluator;

/**
 * This class handles interaction with the evaluator to create 
 * custom variables in the visualization
 */
public class CustVarHandler {

    VizState _vizState;
    String _moduleName;

    CustVarHandler(VizState vizState_, String moduleName_) {
        _vizState = vizState_;
        _moduleName = moduleName_;
    }

    /**
     * attempts to add defined variable
     * 
     * @return null if evaluation was successful; otherwise, returns error string
     */
    String addCustVar(CustVar var) {
        AlloyEvaluator ae = _vizState.getAE();
        return ae.vizEvaluate(var, this, false);
    }
    
    /**
     * attempts to prompt the evaluator to see if the edit will result in a 
     * valid expression during evaluation
     *
     */
    String validateCustVar(CustVar var) {
        AlloyEvaluator ae = _vizState.getAE();
        return ae.vizEvaluate(var, this, true);
    }

    void processAllCustVars() {
        AlloyEvaluator ae = _vizState.getAE();
        Set vars = _vizState.getCustVars(_moduleName);

        if (vars != null) {
            for (Iterator v = vars.iterator(); v.hasNext();) {
                CustVar var = (CustVar)v.next();
                addCustVar(var);
            }
        }
    }

    /**
     * compiles information and adds it to the viz instance
     * 
     * @param var the cust var whose information is being processed
     * @param eltList a List of AlloySet or AlloyRelation, generated by the custvar
     * @param atomLists a List of List of AlloyAtom's or AlloyTuple's, each of which corresponds to 
     * an element in eltList
     */
    public void processAtoms(CustVar var, List eltList, List atomLists) {
        VizInstance inst = _vizState.getInstance();
        AlloyModule module = inst.getModel().getModuleByName(_moduleName);

        if (eltList.get(0) instanceof AlloySet) {
            Iterator atomListsIter = atomLists.iterator();
            for (Iterator setIter = eltList.iterator(); setIter.hasNext();) {
                AlloySet curSet = (AlloySet)setIter.next();
                curSet.setCustom(true);
                _vizState.addCustEltMapping(var, curSet);
                inst.addSet(curSet, _moduleName);

                AlloyModule vsModule = _vizState.getUnprojectedModel().getModuleByName(_moduleName);
                if (vsModule != module) {
                    // if they're not the same object, have to change this too
                    vsModule.addSet(curSet);
                }

                // add atoms
                List atoms = (List)atomListsIter.next();
                for (Iterator atomIter = atoms.iterator(); atomIter.hasNext();) {
                    AlloyAtom curAtom = (AlloyAtom)atomIter.next();
                    inst.addAtomToSet(curAtom, curSet);
                }

            }
        } else {
            Iterator tupleListsIter = atomLists.iterator();
            for (Iterator relIter = eltList.iterator(); relIter.hasNext();) {
                AlloyRelation curRel = (AlloyRelation)relIter.next();
                curRel.setCustom(true);
                _vizState.addCustEltMapping(var, curRel);
                inst.addRelation(curRel, _moduleName);

                AlloyModule vsModule = _vizState.getUnprojectedModel().getModuleByName(_moduleName);
                if (vsModule != module) {
                    // if they're not the same object, have to change this too
                    vsModule.addRelation(curRel);
                }
                
                // add tuples
                List tuples = (List)tupleListsIter.next();
                for (Iterator tupIter = tuples.iterator(); tupIter.hasNext(); ) {
                	AlloyTuple curTuple = (AlloyTuple)tupIter.next();
					inst.addTupleToRelation(curTuple, curRel);
                }
            }

        }

    }

    /*public void processAtoms(CustVar var, List types, List tuples) {
        //System.out.println("TYPES:\n"+types.toString()+"\nTUPLES\n"+tuples.toString());
        Instance inst = _vizState.getInstance();
        AlloyModule module = inst.getModel().getModuleByName(_moduleName);

        if (types.size() == 1) {
            // it's a set
            AlloyType type = new AlloyType((String)types.get(0));
            AlloySet set = new AlloySet(var.getName(), type);
            set.setCustom(true);
            _vizState.addCustEltMapping(var, set);

            inst.addSet(set, _moduleName);
            AlloyModule vsModule = _vizState.getUnprojectedModel().getModuleByName(_moduleName);
            if (vsModule != module) {
                // if they're not the same object, have to change this too
                vsModule.addSet(set);
            }

            for (Iterator atoms = tuples.iterator(); atoms.hasNext();) {
                String name = (String) ((List)atoms.next()).get(0);
                int index = extractIndex(name);
                AlloyAtom atom = new AlloyAtom(name, type, index);
                inst.addAtomToSet(atom, set);
            }

        } else {
            // it's a relation
            List alyTypes = new ArrayList();
            for (Iterator typeIter = types.iterator(); typeIter.hasNext();) {
                String typeName = (String)typeIter.next();
                AlloyType newType = new AlloyType(typeName);
                alyTypes.add(newType);
            }
            AlloyRelation rel = new AlloyRelation(var.getName(), alyTypes);
            rel.setCustom(true);

            _vizState.addCustEltMapping(var, rel);

            inst.addRelation(rel, _moduleName);
            AlloyModule vsModule = _vizState.getUnprojectedModel().getModuleByName(_moduleName);
            if (vsModule != module) {
                // if they're not the same object, have to change this too
                vsModule.addRelation(rel);
            }

            for (Iterator tupIter = tuples.iterator(); tupIter.hasNext();) {
                List tuple = (List)tupIter.next();
                List alyAtoms = new ArrayList();
                int i = 0;
                for (Iterator atoms = tuple.iterator(); atoms.hasNext();) {
                    String name = (String)atoms.next();
                    int index = extractIndex(name);
                    AlloyAtom atom = new AlloyAtom(name, (AlloyType)alyTypes.get(i), index);
                    alyAtoms.add(atom);
                    i++;
                }
                AlloyTuple alyTup = new AlloyTuple(alyAtoms);
                inst.addTupleToRelation(alyTup, rel);
            }
        }

    }*/

    /**
     * extracts index of an atom name
     * e.g. extractIndex("Man_0") returns 0
     */
    private int extractIndex(String name) {
        return Integer.parseInt(name.substring(name.lastIndexOf('_') + 1, name.length()));
    }

}
